/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/KeyExchange/DiffieHellmanHandler.cpp,v 1.15 2003/11/22 16:36:54 itsme Exp $
 *
 *
 * This module implements the main diffiehellman protocol engine.
 *
 * the 'state' of the protocol is the current position in code.
 *
 * external events are passed to the protocol via events which are signalled
 * from the 'HandleXXXX' methods.
 *
 *
 * calculation requests are handled by a different threat, 'CalcThread',
 *
 * at startup a threat is started which will start verifying all the crypto code,
 * and primenumbers. normally this will run for about 3 minutes.
 *
 */


#include "CPhone.h" // theApp  .. needs to be included before the others, to avoid 'new' error in stl_evc
#include <windows.h>  // include after cphone, due to afx issues.

/*
todo:
    - we do need an instance id in the key exchange. ( now 2 successive key exchanges may interfere )
       * not really a problem, since keyexchanges only appear every 2^42 data packets.

*/

#include "PhoneOptions.h"

#include <vector>
#include "vectorutils.h"
#include "stringutils.h"
#include "nhThread.h"
#include "debug.h"
#include "bignum.h"
#include "fnaHashFunction.h"

#include "dhCalcNewPublicKeyRequest.h"
#include "dhCalcSharedSecretRequest.h"
//#include "dhCalcVerifySubgroupRequest.h"

#include "CPhoneDlg.h"

#include "DiffieHellmanHandler.h" //...temporarily using old header.
#include "SpipcHandler.h"
#include "PhoneHandler.h"

#include "CryptoVerifier.h"

#include "fnaPRNG.h"

#include "UDCPinger.h"

// macro's to get data from non-aligned addresses in hilo/bigendian/intel/vax order
#define PTRADD(p,n) (((BYTE*)(p))+(n))
#define GETBYTE(p) (*(BYTE*)(p))
#define GETWORD(p) (GETBYTE(p)<<8|GETBYTE(PTRADD(p,1)))
#define GETDWORD(p) (GETWORD(p)<<16|GETWORD(PTRADD(p,2)))

// 2 types of events:
//    - indicating the presence of a state ( syncloss )
//    - indicating a single event has happened.

inline DWORD BIT(int a) { return(1<<a); }
inline DWORD BITS(int a) { return BIT(a); }
inline DWORD BITS(int a, int b) { return BIT(a)|BIT(b); }
inline DWORD BITS(int a, int b, int c) { return BIT(a)|BIT(b)|BIT(c); }
inline DWORD BITS(int a, int b, int c, int d) { return BIT(a)|BIT(b)|BIT(c)|BIT(d); }
inline DWORD BITS(int a, int b, int c, int d, int e) { return BIT(a)|BIT(b)|BIT(c)|BIT(d)|BIT(e); }
inline DWORD BITS(int a, int b, int c, int d, int e, int f) { return BIT(a)|BIT(b)|BIT(c)|BIT(d)|BIT(e)|BIT(f); }


#define MAX_WAIT_EVENTS 8
#define EVENTMASK BITS

enum {
    M_MY_SECRET,
    M_MY_PUBLIC,
    M_OTHER_PUBLIC,
    M_SHARED_SECRET,
    M_OTHER_VERSION,
    M_OTHER_PUBLIC_HASH,
    M_OTHER_NONCE,
};
inline void CLEARBITS(DWORD& f, int a) { f &= ~BITS(a); }
inline void CLEARBITS(DWORD& f, int a, int b) { f &= ~BITS(a,b); }
inline void CLEARBITS(DWORD& f, int a, int b, int c) { f &= ~BITS(a,b,c); }

inline void SETBITS(DWORD& f, int a) { f |= BITS(a); }
inline void SETBITS(DWORD& f, int a, int b) { f |= BITS(a,b); }
inline void SETBITS(DWORD& f, int a, int b, int c) { f |= BITS(a,b,c); }

inline bool TESTALL(DWORD f, int a) { return (f&BITS(a))==BITS(a); }
inline bool TESTALL(DWORD f, int a, int b) { return (f&BITS(a,b))==BITS(a,b); }
inline bool TESTALL(DWORD f, int a, int b, int c) { return (f&BITS(a,b,c))==BITS(a,b,c); }

//------------------------------------------------------------------------
//  here the parameters of the diffiehellman are defined
//------------------------------------------------------------------------
#if 0

// old choice ... now replaced by new values, after discussion with niels.
Number GetModulusPrime()
{
    Number p;
    p.setbit(4095); 
    p += 19520775;

    return p;
}
// old choice ... now replaced by new values, after discussion with niels.
Number GetSubgroupPrime()
{
    Number q(GetModulusPrime());
    q -= 1;
    q /= 2;
    return q;
}
// old choice ... now replaced by new values, after discussion with niels.
Number GetSubgroupGenerator()
{
    return Number(3);
}
#endif
/*
 *  we chose a prime of the form: p = 2*q+1  with 'q' also prime.
 *
 *  the group 'p'  has 3 subgroups:
 *     one of size p-1 ( consisting of all elements of p )
 *     one of size q-1 ( consisting of all squares - all even elements from p )
 *     one of size 2   ( generated by p-1, consisting of '1' and 'p-1' )
 *
 * we chose the generator, such that it generates half the group 'p',
 *    ( meaning:  g^q==1 )
 *
 * [ NF suggested the opposite, mathematicians are still fighting about this
 *   descision ]
 *
 * this will cause one bit of information leakage from the secret:
 *
 * if the secret is even, an eavesdropper can determine of the public value
 *   ( g^secret mod p ) is a square using the legendre symbol.
 *
 * yielding the value of the least significant bit of the secret.
 *
 * this we do not mind. ( 1 bit of 256 bits lost )
 *
 *
 * also we need to verify that our public values are not accidentally in the
 * subgroup of size '2' : by checking:  1 < value < p-1
 *
 * when p=2q+1 we do not need to verify that the public value is in the subgroup
 * 'q'  ( pubval^q==1 )
 *
 *
 *  p = 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
 *      ( this prime is taken from rfc3526, MODP-16 )
 *  q = (p-1)/2
 *  g = 2
 *
 */
static BYTE g_p_prime[]= {
0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF, 0xC9,0x0F,0xDA,0xA2, 0x21,0x68,0xC2,0x34, 0xC4,0xC6,0x62,0x8B, 0x80,0xDC,0x1C,0xD1,
0x29,0x02,0x4E,0x08, 0x8A,0x67,0xCC,0x74, 0x02,0x0B,0xBE,0xA6, 0x3B,0x13,0x9B,0x22, 0x51,0x4A,0x08,0x79, 0x8E,0x34,0x04,0xDD,
0xEF,0x95,0x19,0xB3, 0xCD,0x3A,0x43,0x1B, 0x30,0x2B,0x0A,0x6D, 0xF2,0x5F,0x14,0x37, 0x4F,0xE1,0x35,0x6D, 0x6D,0x51,0xC2,0x45,
0xE4,0x85,0xB5,0x76, 0x62,0x5E,0x7E,0xC6, 0xF4,0x4C,0x42,0xE9, 0xA6,0x37,0xED,0x6B, 0x0B,0xFF,0x5C,0xB6, 0xF4,0x06,0xB7,0xED,
0xEE,0x38,0x6B,0xFB, 0x5A,0x89,0x9F,0xA5, 0xAE,0x9F,0x24,0x11, 0x7C,0x4B,0x1F,0xE6, 0x49,0x28,0x66,0x51, 0xEC,0xE4,0x5B,0x3D,
0xC2,0x00,0x7C,0xB8, 0xA1,0x63,0xBF,0x05, 0x98,0xDA,0x48,0x36, 0x1C,0x55,0xD3,0x9A, 0x69,0x16,0x3F,0xA8, 0xFD,0x24,0xCF,0x5F,
0x83,0x65,0x5D,0x23, 0xDC,0xA3,0xAD,0x96, 0x1C,0x62,0xF3,0x56, 0x20,0x85,0x52,0xBB, 0x9E,0xD5,0x29,0x07, 0x70,0x96,0x96,0x6D,
0x67,0x0C,0x35,0x4E, 0x4A,0xBC,0x98,0x04, 0xF1,0x74,0x6C,0x08, 0xCA,0x18,0x21,0x7C, 0x32,0x90,0x5E,0x46, 0x2E,0x36,0xCE,0x3B,
0xE3,0x9E,0x77,0x2C, 0x18,0x0E,0x86,0x03, 0x9B,0x27,0x83,0xA2, 0xEC,0x07,0xA2,0x8F, 0xB5,0xC5,0x5D,0xF0, 0x6F,0x4C,0x52,0xC9,
0xDE,0x2B,0xCB,0xF6, 0x95,0x58,0x17,0x18, 0x39,0x95,0x49,0x7C, 0xEA,0x95,0x6A,0xE5, 0x15,0xD2,0x26,0x18, 0x98,0xFA,0x05,0x10,
0x15,0x72,0x8E,0x5A, 0x8A,0xAA,0xC4,0x2D, 0xAD,0x33,0x17,0x0D, 0x04,0x50,0x7A,0x33, 0xA8,0x55,0x21,0xAB, 0xDF,0x1C,0xBA,0x64,
0xEC,0xFB,0x85,0x04, 0x58,0xDB,0xEF,0x0A, 0x8A,0xEA,0x71,0x57, 0x5D,0x06,0x0C,0x7D, 0xB3,0x97,0x0F,0x85, 0xA6,0xE1,0xE4,0xC7,
0xAB,0xF5,0xAE,0x8C, 0xDB,0x09,0x33,0xD7, 0x1E,0x8C,0x94,0xE0, 0x4A,0x25,0x61,0x9D, 0xCE,0xE3,0xD2,0x26, 0x1A,0xD2,0xEE,0x6B,
0xF1,0x2F,0xFA,0x06, 0xD9,0x8A,0x08,0x64, 0xD8,0x76,0x02,0x73, 0x3E,0xC8,0x6A,0x64, 0x52,0x1F,0x2B,0x18, 0x17,0x7B,0x20,0x0C,
0xBB,0xE1,0x17,0x57, 0x7A,0x61,0x5D,0x6C, 0x77,0x09,0x88,0xC0, 0xBA,0xD9,0x46,0xE2, 0x08,0xE2,0x4F,0xA0, 0x74,0xE5,0xAB,0x31,
0x43,0xDB,0x5B,0xFC, 0xE0,0xFD,0x10,0x8E, 0x4B,0x82,0xD1,0x20, 0xA9,0x21,0x08,0x01, 0x1A,0x72,0x3C,0x12, 0xA7,0x87,0xE6,0xD7,
0x88,0x71,0x9A,0x10, 0xBD,0xBA,0x5B,0x26, 0x99,0xC3,0x27,0x18, 0x6A,0xF4,0xE2,0x3C, 0x1A,0x94,0x68,0x34, 0xB6,0x15,0x0B,0xDA,
0x25,0x83,0xE9,0xCA, 0x2A,0xD4,0x4C,0xE8, 0xDB,0xBB,0xC2,0xDB, 0x04,0xDE,0x8E,0xF9, 0x2E,0x8E,0xFC,0x14, 0x1F,0xBE,0xCA,0xA6,
0x28,0x7C,0x59,0x47, 0x4E,0x6B,0xC0,0x5D, 0x99,0xB2,0x96,0x4F, 0xA0,0x90,0xC3,0xA2, 0x23,0x3B,0xA1,0x86, 0x51,0x5B,0xE7,0xED,
0x1F,0x61,0x29,0x70, 0xCE,0xE2,0xD7,0xAF, 0xB8,0x1B,0xDD,0x76, 0x21,0x70,0x48,0x1C, 0xD0,0x06,0x91,0x27, 0xD5,0xB0,0x5A,0xA9,
0x93,0xB4,0xEA,0x98, 0x8D,0x8F,0xDD,0xC1, 0x86,0xFF,0xB7,0xDC, 0x90,0xA6,0xC0,0x8F, 0x4D,0xF4,0x35,0xC9, 0x34,0x06,0x31,0x99,
0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF
};
#define P_PRIMELEN sizeof(g_p_prime)

Number GetModulusPrime()
{
    Number p;
    p.FromByteVector(ByteVector(g_p_prime, g_p_prime+P_PRIMELEN));

    debug("p= %hs\n", p.AsString().c_str());
    return p;
}
Number GetSubgroupPrime()
{
    Number q= GetModulusPrime();
    
    q -= 1;

    q /= 2;

    debug("q= %hs\n", q.AsString().c_str());
    return q;
}

Number GetSubgroupGenerator()
{
    return Number(2);
}

DiffieHellmanHandler::DiffieHellmanHandler(CCPhoneDlg& dlg) : 
    m_dlg(dlg), 
    m_calcrequests(1), 
    m_calc(dlg, m_calcrequests),
    m_modulus_prime(GetModulusPrime()),
    m_subgroup_prime(GetSubgroupPrime()),
    m_subgroup_generator(GetSubgroupGenerator()),
    m_verifier(m_modulus_prime, m_subgroup_prime, m_subgroup_generator)
{
    for (int i=0 ; i<NR_OF_EVENTS ; i++)
        m_hEvents[i]= CreateEvent(0, TRUE, FALSE, 0);

    m_dwValidityFlags= 0;
    m_dwCurrentWait= 0;

    m_my_secret= 0;
    m_my_public= 0;
    m_other_public= 0;
    m_shared_secret= 0;

    m_my_version= 1;
    m_other_version= 0;
    m_negotiated_version= 0;

    m_bIAmTheOriginator= false;

    m_bInSync= false;

    m_other_public_hash.clear();

    InitializeCriticalSection(&m_lock);

    theApp.m_ud->RegisterUserdataHandler(UDCLT_DIFFIEHELLMAN, this);
}

DiffieHellmanHandler::~DiffieHellmanHandler()
{
    theApp.m_ud->UnRegisterUserdataHandler(UDCLT_DIFFIEHELLMAN);

    DeleteCriticalSection(&m_lock);
}

DWORD DiffieHellmanHandler::ThreadProc()
{
    mainDiffieHellmanThread();

    return 0;
}

// start all threads
void DiffieHellmanHandler::StartThread()
{
    debug("starting calc thread\n");
    m_calc.StartThread();
    debug("calc thread started\n");

    debug("starting verify thread\n");
    m_verifier.StartThread();
    debug("verify thread started\n");

    debug("starting diffiehellman thread\n");
    NHThread::StartThread();
    debug("diffiehellman thread started\n");
}

void DiffieHellmanHandler::SetHighCalcPriority()
{
    m_calc.SetHighPriority();
}
void DiffieHellmanHandler::SetLowCalcPriority()
{
    m_calc.SetLowPriority();
}

bool DiffieHellmanHandler::StartPubPrivKeyCalc()
{
    debug("DH: StartPubPrivKeyCalc\n");
    ResetEvent(m_hEvents[EV_PKCALC_OK]);
    ResetEvent(m_hEvents[EV_PKCALC_FAIL]);
    return m_calcrequests.Add(new CalcNewPublicKeyRequest(
                m_subgroup_generator, m_modulus_prime, m_subgroup_prime,
                m_my_secret, m_my_public, 
                m_hEvents[EV_PKCALC_OK], m_hEvents[EV_PKCALC_FAIL]));
}
bool DiffieHellmanHandler::StartSharedSecretCalc()
{
    debug("DH: StartSharedSecretCalc\n");
    ResetEvent(m_hEvents[EV_ZZCALC_OK]);
    ResetEvent(m_hEvents[EV_ZZCALC_FAIL]);
    return m_calcrequests.Add(new CalcSharedSecretRequest(
                m_my_secret, m_other_public, m_modulus_prime, m_subgroup_prime, 
                m_shared_secret,
                m_hEvents[EV_ZZCALC_OK], m_hEvents[EV_ZZCALC_FAIL]));
}

#if 0
bool DiffieHellmanHandler::StartVerifySubgroupCalc()
{
    debug("DH: StartVerifySubgroupCalc\n");
    ResetEvent(m_hEvents[EV_SGVERIFY_OK]);
    ResetEvent(m_hEvents[EV_SGVERIFY_FAIL]);
    return m_calcrequests.Add(new CalcVerifySubgroupRequest(
                m_modulus_prime, m_subgroup_prime, m_my_public, 
                m_hEvents[EV_SGVERIFY_OK], m_hEvents[EV_SGVERIFY_FAIL]));
}
#endif

bool DiffieHellmanHandler::isVersionOk()
{
    return (m_other_version==m_my_version);
}
bool DiffieHellmanHandler::isVersionTooNew()
{
    return (m_other_version>m_my_version);
}
bool DiffieHellmanHandler::isVersionTooOld()
{
    return (m_other_version<m_my_version);
}

void DiffieHellmanHandler::SendStartMessage()
{
    debug("DH: SendStartMessage\n");
    ResetReceiveEvents();

    ByteVector pubhash= HashFunction::Calculate(m_my_public.ToByteVector());
    debug("DH:   pubhashlen=%d\n", pubhash.size());

    ByteVector msg;
    msg.push_back((BYTE)DHMSG_START);
    msg.push_back((BYTE)m_my_version);

    msg.insert(msg.end(), pubhash.begin(), pubhash.end());

    theApp.m_ud->SendStateMessage(UDCLT_DIFFIEHELLMAN, msg, g_options.dhresend_delay);

    // these pings cause the line to be initially synced
    theApp.m_pinger->SendPings(2, 500, 32);
}
void DiffieHellmanHandler::SendContinueMessage()
{
    debug("DH: SendContinueMessage\n");

    ByteVector nrdata= m_my_public.ToByteVector();

    ByteVector msg;
    msg.push_back(DHMSG_CONTINUE);
    msg.insert(msg.end(), nrdata.begin(), nrdata.end());

    theApp.m_ud->SendStateMessage(UDCLT_DIFFIEHELLMAN, msg, g_options.dhresend_delay);
}
void DiffieHellmanHandler::SendEndMessage()
{
    debug("DH: SendEndMessage\n");
    ByteVector nrdata= m_my_public.ToByteVector();

    ByteVector msg;
    msg.push_back(DHMSG_END);
    msg.insert(msg.end(), nrdata.begin(), nrdata.end());

    theApp.m_ud->SendStateMessage(UDCLT_DIFFIEHELLMAN, msg, g_options.dhresend_delay);
}
void DiffieHellmanHandler::SendConfirmMessage()
{
    debug("DH: SendConfirmMessage\n");
    ByteVector nonce;

    if (!theApp.m_rng->RandomData(128, nonce))
        return;  //  .... todo: handle error

    ByteVector msg;
    msg.push_back(DHMSG_CONFIRM);
    msg.insert(msg.end(), nonce.begin(), nonce.end());

    theApp.m_ud->SendStateMessage(UDCLT_DIFFIEHELLMAN, msg, g_options.dhresend_delay);
}

void DiffieHellmanHandler::SendRejectMessage()
{
    debug("DH: SendRejectMessage\n");
    ByteVector msg;
    msg.push_back(DHMSG_REJECT);

    theApp.m_ud->SendStateMessage(UDCLT_DIFFIEHELLMAN, msg, g_options.dhresend_delay);
}

bool DiffieHellmanHandler::StopSending()
{
    debug("DH: StopSending\n");
    return theApp.m_ud->StopStateMessage(UDCLT_DIFFIEHELLMAN);
}
bool DiffieHellmanHandler::VerifyHash()
{
    debug("other calc hash: %hs\n", hexdump(HashFunction::Calculate(m_other_public.ToByteVector())).c_str());
    debug("other sent hash: %hs\n", hexdump(m_other_public_hash).c_str());

    return HashFunction::Calculate(m_other_public.ToByteVector())==m_other_public_hash;
}
DWORD GetLastDword(ByteVector sighash)
{
    DWORD result= 0;

    ByteVector::reverse_iterator hi= sighash.rbegin();
    for (int i=0 ; i<4 ; i++, ++hi)
    {
        result |= (*hi)<<(i*8);
    }
    return result;
}

void DiffieHellmanHandler::SetNewKey()
{
    ByteVector keydata= m_shared_secret.ToByteVector();
    keydata.push_back(m_negotiated_version);

    ByteVector keyhash= HashFunction::Calculate(keydata);

    theApp.m_ipc->SetMachineKey(keyhash, m_bIAmTheOriginator);

    keyhash.push_back(ZZ_KEYSIG_PAD);

    ByteVector keysighash= HashFunction::Calculate(keyhash);

    DWORD lastword= GetLastDword(keysighash);

    std::string keysig;
    for (int i=0 ; i< KEYSIG_LENGTH ; ++i)
    {
        keysig += (char)('A'+(lastword%26));

        lastword/=26;
    }

    m_dlg.SetKeySignature(keysig, m_bIAmTheOriginator);
}

static std::string eventname(int n)
{
    switch(n)
    {
    case EV_PKCALC_OK: return "PKCALC_OK";
    case EV_PKCALC_FAIL: return "PKCALC_FAIL";
    case EV_SGVERIFY_OK: return "SGVERIFY_OK";
    case EV_SGVERIFY_FAIL: return "SGVERIFY_FAIL";
    case EV_ZZCALC_OK: return "ZZCALC_OK";
    case EV_ZZCALC_FAIL: return "ZZCALC_FAIL";
    case EV_RCV_START: return "RCV_START";
    case EV_RCV_END: return "RCV_END";
    case EV_RCV_CONTINUE: return "RCV_CONTINUE";
    case EV_RCV_CONFIRM: return "RCV_CONFIRM";
    case EV_RCV_REJECT: return "RCV_REJECT";
    case EV_SYNCLOSS: return "SYNCLOSS";
    case EV_USERREQUEST: return "USERREQUEST";
    case EV_USERABORT: return "USERABORT";
    default:
        return stringformat("EVENT-%d-UNKNOWN", n);
    }
}
// todo: extract the 'WaitState' stuff to a 'StateMachine' class
int DiffieHellmanHandler::WaitState(DWORD eventMask)
{
    HANDLE hWaitEvents[MAX_WAIT_EVENTS];
    int nEventIds[MAX_WAIT_EVENTS];

    // allways expect abort.
    m_dwCurrentWait= eventMask;

    debug("DH-WS: waiting for");
    DWORD j=0;
    for (int i=0 ; i<32 ; i++)
    {
        if (m_dwCurrentWait&(1<<i))  {
            if (j<MAX_WAIT_EVENTS)
            {
                debug(" %d=%hs", j, eventname(i).c_str());

                nEventIds[j]= i;
                hWaitEvents[j]= m_hEvents[i];

                j++;
            }
            else
                debug("DH: ERROR - too many events in wait\n");
        }
    }
    debug("\n");


    DWORD res= WaitForMultipleObjects(j, hWaitEvents, false, INFINITE);
    if (res>=WAIT_OBJECT_0 && res<WAIT_OBJECT_0+j)
    {
        debug("DH-WS: event %d=%hs\n", res-WAIT_OBJECT_0, eventname(nEventIds[res-WAIT_OBJECT_0]).c_str());
        ResetEvent(hWaitEvents[res-WAIT_OBJECT_0]);
        return nEventIds[res-WAIT_OBJECT_0];
    }

    if (res>=WAIT_ABANDONED_0  && res<WAIT_ABANDONED_0 +j)
    {
        debug("DH-WaitForMultipleObjects ERROR %hs abandoned wait", eventname(nEventIds[res-WAIT_ABANDONED_0]).c_str());
        return -1;
    }
    if (res==WAIT_TIMEOUT)
        debug("DH-WaitForMultipleObjects ERROR timeout\n");
    else if (res==WAIT_FAILED)
        error("DH-WaitForMultipleObjects ERROR failed ");
    else
        error("DH-WaitForMultipleObjects error ");

    return -1;
}

void DiffieHellmanHandler::ClearState()
{
    EnterCriticalSection(&m_lock);

    debug("DH: clearing dh-state\n");
    m_my_secret= 0;
    m_my_public= 0;
    m_other_public= 0;
    m_shared_secret= 0;
    m_other_version= 0;
    m_other_public_hash.clear();
    m_dwValidityFlags= 0;

    LeaveCriticalSection(&m_lock);
}
void DiffieHellmanHandler::ResetReceiveEvents()
{
    ResetEvent(m_hEvents[EV_RCV_START]);
    ResetEvent(m_hEvents[EV_RCV_CONTINUE]);
    ResetEvent(m_hEvents[EV_RCV_CONFIRM]);
    ResetEvent(m_hEvents[EV_RCV_END]);
    ResetEvent(m_hEvents[EV_RCV_REJECT]);
}
//----------------------------------------------------------------
void DiffieHellmanHandler::mainDiffieHellmanThread()
{
    int n=0;

    debug("DH: starting main diffie hellman protocol thread\n");

    while(n!=-1)
    {
        ClearState();

        SetLowCalcPriority();
        StartPubPrivKeyCalc();
        if (!WaitForPubPrivKeyCalc())
            continue;

        EnterCriticalSection(&m_lock);
        SETBITS(m_dwValidityFlags, M_MY_SECRET,M_MY_PUBLIC);
        LeaveCriticalSection(&m_lock);

        debug("DH: got new pk\n");

        m_bIAmTheOriginator= false;

        ResetReceiveEvents();

        if (!WaitForNewKeyExchange())
            theApp.m_phone->SignalKeyExchangeFailure();
        else
        {
#if 0
            StartVerifySubgroupCalc();
            if (!WaitForSubgroupCalc())
                theApp.m_phone->SignalKeyExchangeFailure();
#endif

            theApp.m_phone->SignalKeyExchangeDone();
        }

        StopSending();
    }
}
bool DiffieHellmanHandler::WaitForPubPrivKeyCalc()
{
    // todo: wait is not INFINITE, too long wait means problem in calc thread.
    int n= WaitState(EVENTMASK(EV_PKCALC_OK,EV_PKCALC_FAIL,EV_USERABORT));
    switch(n)
    {
    case EV_PKCALC_OK:
        // public key generated
        return true;

    case EV_PKCALC_FAIL:
        debug("DH-ERROR: error generating public key\n", eventname(n).c_str());
        return false;
    case EV_USERABORT:
        debug("DH-ERROR: aborted\n", eventname(n).c_str());
        return false;
    default:
        debug("DH-ERROR: invalid event %hs, expected pkcalc\n", eventname(n).c_str());
        return false;
    }
}
bool DiffieHellmanHandler::WaitForSubgroupCalc()
{
    // todo: wait is not INFINITE, too long wait means problem in calc thread.
    int n= WaitState(EVENTMASK(EV_SGVERIFY_OK,EV_SGVERIFY_FAIL,EV_USERABORT));
    switch(n)
    {
    case EV_SGVERIFY_OK:
        // subgroup verified ok.
        return true;

    case EV_SGVERIFY_FAIL:
        debug("DH-ERROR: error verifying subgroup\n", eventname(n).c_str());
        return false;
    case EV_USERABORT:
        debug("DH-ERROR: aborted\n", eventname(n).c_str());
        return false;
    default:
        debug("DH-ERROR: invalid event %hs, expected pkcalc\n", eventname(n).c_str());
        return false;
    }
}

bool DiffieHellmanHandler::WaitForNewKeyExchange()
{
    int n= WaitState(EVENTMASK(EV_USERREQUEST, EV_RCV_START,EV_USERABORT));
    //ResetEvent(m_hEvents[EV_USERREQUEST]);

    debug("DH: starting protocol run\n");
    
    switch(n)
    {
        case EV_USERREQUEST:
            return BeAlice();

        case EV_RCV_START: 
            return BeBop();

        default:
            debug("DH-ERROR: invalid event %hs, expected user, or start\n", eventname(n).c_str());
            return false;
    }
}

bool DiffieHellmanHandler::BeBop(int recursionlevel/*= 0*/)
{
    debug("DH: being bob %d\n", recursionlevel);

    if (isVersionTooNew())
    {
        ClearState();
        StartPubPrivKeyCalc();
        if (!WaitForPubPrivKeyCalc())
            return false;
        EnterCriticalSection(&m_lock);
        SETBITS(m_dwValidityFlags, M_MY_SECRET,M_MY_PUBLIC);
        LeaveCriticalSection(&m_lock);

        return BeAlice(recursionlevel+1);
    }
    else if (isVersionTooOld())
    {
        SendRejectMessage();

        WaitForHangup();

        return false;
    }
    else if (!isVersionOk())
    {
        debug("DH-ERROR: unexpected version case  me=%d other=%d\n", m_my_version, m_other_version);
        return false;
    }

    SendContinueMessage();

    if (!WaitForEndMessage())
        return false;

    // make sure the  1 < pv < p-1
    //  .. this is to make sure it is in the right subgroup.
    if (m_other_public<=1 || m_other_public>=m_modulus_prime-1)
    {
        debug("DH: invalid public key received\n");
        return false;
    }

    if (!VerifyHash())
    {
        debug("DH-ERROR: hash does not match alice's public key\n");
        return false;
    }

    StartSharedSecretCalc();
    if (!WaitForSharedSecret())
        return false;
    EnterCriticalSection(&m_lock);
    SETBITS(m_dwValidityFlags, M_SHARED_SECRET);
    LeaveCriticalSection(&m_lock);

    // this causes alice to lose sync.
    SetNewKey();

    // this message should cause the other side to lose and re-find sync quickly
    SendConfirmMessage();
    //if (!WaitForConfirm())
    //    return false;

    return true;
}

enum { DH_DOREVERSAL, DH_DOCONTINUE, DH_DOABORT };

bool DiffieHellmanHandler::BeAlice(int recursionlevel/*= 0*/)
{
    debug("DH: being alice %d\n", recursionlevel);
    SendStartMessage();

    switch(WaitForContinueOrReversal())
    {
        case DH_DOREVERSAL:
            ClearState();
            StartPubPrivKeyCalc();
            if (!WaitForPubPrivKeyCalc())
                return false;
            EnterCriticalSection(&m_lock);
            SETBITS(m_dwValidityFlags, M_MY_SECRET,M_MY_PUBLIC);
            LeaveCriticalSection(&m_lock);

            return BeBop(recursionlevel+1);

        case DH_DOCONTINUE:

            // make sure the  1 < pv < p-1
            if (m_other_public<=1 || m_other_public>=m_modulus_prime-1)
            {
                debug("DH: invalid public key received\n");
                return false;
            }
            // continue
            break;
        case DH_DOABORT:
            return false;
        default:
            return false;
    }

    SendEndMessage();

    // this happens while bob is receiving the end msg, and calcing key.
    StartSharedSecretCalc();
    if (!WaitForSharedSecret())
        return false;
    EnterCriticalSection(&m_lock);
    SETBITS(m_dwValidityFlags, M_SHARED_SECRET);
    LeaveCriticalSection(&m_lock);

    if (!WaitForSyncLoss())
        return false;

    SetNewKey();

    SendConfirmMessage();
    //if (!WaitForConfirm())
    //    return false;

    return true;
}

bool DiffieHellmanHandler::WaitForHangup()
{
    int n= WaitState(EVENTMASK(EV_USERABORT));
    switch(n)
    {
    case EV_USERABORT:
        return false;
    default:
        debug("DH-ERROR: invalid event %hs, expected abort\n", eventname(n).c_str());
        return false;
    }
}
bool DiffieHellmanHandler::WaitForEndMessage()
{
    int n= WaitState(EVENTMASK(EV_RCV_END,EV_USERABORT));
    StopSending();
    switch(n)
    {
    case EV_RCV_END:
        // continue to next state
        return true;
    case EV_USERABORT:
        debug("DH-: keyexchange aborted\n");
        return false;
    default:
        debug("DH-ERROR: invalid event %hs, expected end\n", eventname(n).c_str());
        return false;
    }
}
bool DiffieHellmanHandler::WaitForSharedSecret()
{
    int n= WaitState(EVENTMASK(EV_ZZCALC_OK, EV_ZZCALC_FAIL, EV_USERABORT));
    switch(n)
    {
    case EV_ZZCALC_OK:
        // continue to next state
        return true;
    case EV_ZZCALC_FAIL:
        debug("DH-: shared secret calculation failed\n");
        return false;
    case EV_USERABORT:
        debug("DH-: keyexchange aborted\n");
        return false;
    default:
        debug("DH-ERROR: invalid event %hs, expected zz\n", eventname(n).c_str());
        return false;
    }
}

int DiffieHellmanHandler::WaitForContinueOrReversal()
{
    int n= WaitState(EVENTMASK(EV_RCV_CONTINUE, EV_RCV_START, EV_RCV_REJECT,EV_USERABORT));
    StopSending();
    switch(n)
    {
        case EV_RCV_CONTINUE:
            return DH_DOCONTINUE;
        case EV_RCV_START:
            return DH_DOREVERSAL;
        case EV_RCV_REJECT:
            // this causes the phone handler to hangup, and thus
            // signal to the other side that we have received their reject.
            return DH_DOABORT;

        case EV_USERABORT:
            return DH_DOABORT;

        default:
            debug("DH-ERROR: invalid event %hs, expected continue, start or reject\n", eventname(n).c_str());
            return DH_DOABORT;
    }
}
bool DiffieHellmanHandler::WaitForSyncLoss()
{
    debug("dh state: sync=%d\n", m_bInSync);
    int n= WaitState(EVENTMASK(EV_SYNCLOSS,EV_USERABORT));
    StopSending();
    switch(n)
    {
    case EV_SYNCLOSS:
        // continue
        return true;

    case EV_USERABORT:
        return false;

    default:
        debug("DH-ERROR: invalid event %hs, expected syncloss\n", eventname(n).c_str());
        return false;
    }
}

//....................................
bool DiffieHellmanHandler::mayReceiveConfirmMessage()
{
    return TESTALL(m_dwCurrentWait, EV_RCV_CONFIRM);
}
bool DiffieHellmanHandler::mayReceiveEndMessage()
{
    return TESTALL(m_dwCurrentWait, EV_RCV_END);
}
bool DiffieHellmanHandler::mayReceiveContinueMessage()
{
    return TESTALL(m_dwCurrentWait, EV_RCV_CONTINUE);
}
bool DiffieHellmanHandler::mayReceiveStartMessage()
{
    return TESTALL(m_dwCurrentWait, EV_RCV_START);
}
//-----------------------------------------------------------------------
//   userdata message handling

// this static method dispatches keyexchange messages to the
// correct instance of the diffiehellman handler
//    [ there is currently only 1, so that is easy ]

// todo: currently nothing is done when invalid messages are received.
//       they are just ignored. ( result code of handlers is false
//       when this is so )
bool DiffieHellmanHandler::HandleUserdataMessage(const ByteVector& data)
{
    debug("dh userdata msg %02x [l=%d]\n", data[0], data.size());
    switch(data[0])
    {
    case DHMSG_START:
        {
        ByteVector alicehash(data.begin()+2, data.end());
        debug("DH-starthashlen=%d\n", alicehash.size());
        return theApp.m_dh->HandleStartMessage(data[1], alicehash);
        }
        break;
    case DHMSG_CONTINUE:
        {
        Number nr;
        nr.FromByteVector(ByteVector(data.begin()+1, data.end()));
        return theApp.m_dh->HandleContinueMessage(nr);
        }
        break;
    case DHMSG_END:
        {
        Number nr;
        nr.FromByteVector(ByteVector(data.begin()+1, data.end()));
        return theApp.m_dh->HandleEndMessage(nr);
        }
        break;
    case DHMSG_CONFIRM:
        {
        ByteVector nonce(data.begin()+1, data.end());
        return theApp.m_dh->HandleConfirmMessage(nonce);
        }
        break;
    case DHMSG_REJECT:
        return theApp.m_dh->HandleRejectMessage();
        break;
    }
    return false;
}


bool DiffieHellmanHandler::HandleStartMessage(int version, const ByteVector& publichash)
{
    // check version
    debug("DH:. msgstart vbob=%d valice=%d  hashlen=%d\n", version, m_my_version, publichash.size());

    if (mayReceiveStartMessage())
    {
        EnterCriticalSection(&m_lock);

        if (TESTALL(m_dwValidityFlags, M_OTHER_VERSION,M_OTHER_PUBLIC_HASH)) {
            if (m_other_version!=version || m_other_public_hash!=publichash)
            {
                // we are receiving conflicting 'start' messages
                LeaveCriticalSection(&m_lock);
                return false;
            }
            // just ignore duplicates
            LeaveCriticalSection(&m_lock);
            return true;
        }

        m_other_version= version;
        m_other_public_hash= publichash;
        SETBITS(m_dwValidityFlags, M_OTHER_VERSION,M_OTHER_PUBLIC_HASH);

        LeaveCriticalSection(&m_lock);

        SetEvent(m_hEvents[EV_RCV_START]);

        return true;
    }
    return false;
}

bool DiffieHellmanHandler::HandleContinueMessage(const Number &publicvalue)
{
    debug("DH:. msgcontinue, nr of %d bytes\n", publicvalue.NrOfBytesInNumber());

    if (mayReceiveContinueMessage())
    {
        EnterCriticalSection(&m_lock);

        if (TESTALL(m_dwValidityFlags, M_OTHER_PUBLIC)) {
            if (m_other_public!=publicvalue)
            {
                // we are receiving conflicting 'continue' messages
                LeaveCriticalSection(&m_lock);
                return false;
            }
            // just ignore duplicates
            LeaveCriticalSection(&m_lock);
            return true;
        }

        m_other_public= publicvalue;
        SETBITS(m_dwValidityFlags, M_OTHER_PUBLIC);

        LeaveCriticalSection(&m_lock);

        SetEvent(m_hEvents[EV_RCV_CONTINUE]);

        return true;
    }

    return false;
}
bool DiffieHellmanHandler::HandleEndMessage(const Number &publicvalue)
{
    debug("DH:. msgend, nr of %d bytes\n", publicvalue.NrOfBytesInNumber());
    if (mayReceiveEndMessage())
    {
        EnterCriticalSection(&m_lock);

        if (TESTALL(m_dwValidityFlags, M_OTHER_PUBLIC)) {
            if (m_other_public!=publicvalue)
            {
                // we are receiving conflicting 'end' messages
                LeaveCriticalSection(&m_lock);
                return false;
            }
            // just ignore duplicates
            LeaveCriticalSection(&m_lock);
            return true;
        }

        m_other_public= publicvalue;
        SETBITS(m_dwValidityFlags, M_OTHER_PUBLIC);

        LeaveCriticalSection(&m_lock);

        SetEvent(m_hEvents[EV_RCV_END]);

        return true;
    }
    return false;
}
bool DiffieHellmanHandler::HandleConfirmMessage(const ByteVector &nonce)
{
    debug("DH:. msgconfirm, %d bytes\n", nonce.size());
    if (mayReceiveConfirmMessage())
    {
        EnterCriticalSection(&m_lock);

        if (TESTALL(m_dwValidityFlags, M_OTHER_NONCE)) {
            if (m_other_nonce!=nonce)
            {
                // we are receiving conflicting 'confirm' messages
                LeaveCriticalSection(&m_lock);
                return false;
            }
            // just ignore duplicates
            LeaveCriticalSection(&m_lock);
            return true;
        }

        m_other_nonce= nonce;
        SETBITS(m_dwValidityFlags, M_OTHER_NONCE);

        LeaveCriticalSection(&m_lock);

        SetEvent(m_hEvents[EV_RCV_CONFIRM]);

        return true;
    }
    return false;
}

bool DiffieHellmanHandler::HandleRejectMessage()
{
    SetEvent(m_hEvents[EV_RCV_REJECT]);
    return true;
}

//------------------------------------------------------------------
// signals

// this is called from the spipchandler, to signal that the cryptostream
// sync was lost
bool DiffieHellmanHandler::HandleSyncLoss()
{
    debug("dh: lost sync\n");
    m_bInSync= false;
    PulseEvent(m_hEvents[EV_SYNCLOSS]);
    return true;
}
// this is called from the spipchandler, to signal that the cryptostream
// sync was found again
bool DiffieHellmanHandler::HandleSyncFound()
{
    debug("dh: found sync\n");
    m_bInSync= true;
    return true;
}

// this is called from the phonehandler, when the call was aborted.
bool DiffieHellmanHandler::HandleUserAbort()
{
    PulseEvent(m_hEvents[EV_USERABORT]);
    m_dlg.ClearKeySignature();
    return true;
}

// this is called from the phonehandler, to initiate a keyexchange.
void DiffieHellmanHandler::HandleUserStartRequest()
{
    m_bIAmTheOriginator= true;
    SetHighCalcPriority();
    SetEvent(m_hEvents[EV_USERREQUEST]);
}




